using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;

namespace WPFTemplateLib.Adorners.SmartAdorner
{
	/// <summary>
	/// 可在 Xaml 中添加且支持绑定的 Adorner
	/// 来自《[Design Adorners in XAML with Data Binding Support](https://www.codeproject.com/articles/709266/design-adorners-in-xaml-with-data-binding-support)》
	/// 迁移自：https://gitee.com/DLGCY_Clone/WPF_SmartAdorner
	/// </summary>
	public class SmartAdorner : Adorner
	{
		#region 附加属性

		private static SmartAdorner GetAdorner(DependencyObject obj)
		{
			return (SmartAdorner)obj.GetValue(AdornerProperty);
		}

		private static void SetAdorner(DependencyObject obj, SmartAdorner value)
		{
			obj.SetValue(AdornerProperty, value);
		}

		private static readonly DependencyProperty AdornerProperty =
			 DependencyProperty.RegisterAttached("Adorner", typeof(SmartAdorner), typeof(SmartAdorner), new PropertyMetadata(null));

		public static DataTemplate GetTemplate(DependencyObject obj)
		{
			return (DataTemplate)obj.GetValue(TemplateProperty);
		}

		public static void SetTemplate(DependencyObject obj, DataTemplate value)
		{
			obj.SetValue(TemplateProperty, value);
		}

		public static readonly DependencyProperty TemplateProperty =
			 DependencyProperty.RegisterAttached("Template", typeof(DataTemplate), typeof(SmartAdorner), new PropertyMetadata(null, OnTemplateChanged));

		private static void OnTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			SmartAdorner adorner = GetAdorner(d);
			if (adorner != null)
			{
				adorner.Template = (DataTemplate)e.NewValue;
			}
		}

		public static DataTemplateSelector GetTemplateSelector(DependencyObject obj)
		{
			return (DataTemplateSelector)obj.GetValue(TemplateSelectorProperty);
		}

		public static void SetTemplateSelector(DependencyObject obj, DataTemplateSelector value)
		{
			obj.SetValue(TemplateSelectorProperty, value);
		}

		public static readonly DependencyProperty TemplateSelectorProperty =
			 DependencyProperty.RegisterAttached("TemplateSelector", typeof(DataTemplateSelector), typeof(SmartAdorner), new PropertyMetadata(null, OnTemplateSelectorChanged));

		private static void OnTemplateSelectorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			SmartAdorner adorner = GetAdorner(d);
			if (adorner != null)
			{
				adorner.TemplateSelector = (DataTemplateSelector)e.NewValue;
			}
		}

		public static bool GetVisible(DependencyObject obj)
		{
			return (bool)obj.GetValue(IsVisibleProperty);
		}

		public static void SetVisible(DependencyObject obj, bool value)
		{
			obj.SetValue(VisibleProperty, value);
		}

		public static readonly DependencyProperty VisibleProperty =
			 DependencyProperty.RegisterAttached("Visible", typeof(bool), typeof(SmartAdorner), new PropertyMetadata(false, OnVisibleChanged));

		private static void OnVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			FrameworkElement adornedElement = d as FrameworkElement;
			if (adornedElement == null)
			{
				//throw new InvalidOperationException("Adorners can only be applied to elements deriving from FrameworkElement");
				return;
			};

			AdornerLayer layer = AdornerLayer.GetAdornerLayer(adornedElement);
			if (layer == null)
			{
				//throw new InvalidOperationException("Cannot show adorner since no adorner layer was found in the visual tree");
				Debug.WriteLine($"【异常】未找到 [{adornedElement.GetType().Name}] 的 AdornerLayer。");
				return;
			}

			SmartAdorner adorner = GetAdorner(adornedElement);

			bool isVisible = (bool)e.NewValue;

			if (isVisible && adorner == null)
			{
				adorner = new SmartAdorner(adornedElement);

				SetAdorner(adornedElement, adorner);
				layer.Add(adorner);
			}
			else if (!isVisible && adorner != null)
			{
				layer.Remove(adorner);
				SetAdorner(d, null);
			}
		}

		#endregion

		/// <summary>
		/// 内容
		/// </summary>
		private ContentPresenter _content;

		public SmartAdorner(FrameworkElement adornedElement) : base(adornedElement)
		{
			_content = new ContentPresenter();
			Binding dataContextBinding = new Binding("DataContext")
			{
				Source = adornedElement
			};
			BindingOperations.SetBinding(_content, ContentPresenter.ContentProperty, dataContextBinding);
			Template = GetTemplate(adornedElement);
			TemplateSelector = GetTemplateSelector(adornedElement);
			AddVisualChild(_content);
			AddLogicalChild(_content);
		}

		public DataTemplate Template
		{
			get => _content.ContentTemplate;
			set => _content.ContentTemplate = value;
		}

		public DataTemplateSelector TemplateSelector
		{
			get => _content.ContentTemplateSelector;
			set => _content.ContentTemplateSelector = value;
		}

		/// <inheritdoc />
		protected override int VisualChildrenCount => 1;

		/// <inheritdoc />
		protected override System.Windows.Media.Visual GetVisualChild(int index)
		{
			if (index == 0) return _content;
			throw new ArgumentOutOfRangeException("index");
		}

		/// <inheritdoc />
		protected override Size MeasureOverride(Size constraint)
		{
			_content.Measure(constraint);
			return _content.DesiredSize;
		}

		/// <inheritdoc />
		protected override Size ArrangeOverride(Size finalSize)
		{
			_content.Arrange(new Rect(new Point(0, 0), finalSize));
			return finalSize;
		}
	}
}
